﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using PSE.Framework.Configuration;
using System.Xml.XPath;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;

namespace PSE.Framework.ErrorLogging
{
    [Serializable]
    public class LogData
    {
        [XmlIgnore()]
        public const string SEPARATOR = "------------------------------------------------------------";

        /// <summary>
        /// Sistema onde ocorreu o erro
        /// </summary>
        [XmlAttribute]
        public SystemComponent System { get; set; }
        /// <summary>
        /// Origem do erro no sistema
        /// </summary>
        [XmlAttribute]
        public SystemComponent Source { get; set; }
        /// <summary>
        /// Criticidade do erro
        /// </summary>
        [XmlAttribute]
        public LogType Type { get; set; }
        /// <summary>
        /// Data e hora da ocorrência
        /// </summary>
        [XmlAttribute]
        public DateTime Date { get; set; }

        /// <summary>
        /// Mensagem de erro
        /// </summary>
        [XmlElement]
        public object Message { get; set; }

        /// <summary>
        /// Informações de ambiente
        /// </summary>
        [XmlElement]
        public EnvironmentData Enviroment { get; set; }

        /// <summary>
        /// Erros nos parâmetros informados ao componente de log
        /// </summary>
        [XmlArray("ParameterErrors")]
        [XmlArrayItem("Error")]
        public List<string> ParameterErrors { get; set; }

        /// <summary>
        /// Erros na configuração do componente de log
        /// </summary>
        [XmlArray("ConfigurationErrors")]
        [XmlArrayItem("Error")]
        public List<string> ConfigurationErrors { get; set; }

        /// <summary>
        /// Erros na gravação do erro
        /// </summary>
        [XmlArray("WriteErrors")]
        [XmlArrayItem("Error")]
        public List<string> WriteErrors { get; set; }

        /// <summary>
        /// Erros na exibição do erro
        /// </summary>
        [XmlArray("DisplayErrors")]
        [XmlArrayItem("Error")]
        public List<string> DisplayErrors { get; set; }

        /// <summary>
        /// Erros de empacotamento do erro
        /// </summary>
        [XmlArray("PackagerErrors")]
        [XmlArrayItem("Error")]
        public List<string> PackagerErrors { get; set; }

        /// <summary>
        /// Informações do erro que originou o log
        /// </summary>
        [XmlElement]
        public string InnerLogData { get; set; }

        public override string ToString()
        {
            StringBuilder logData = new StringBuilder();

            logData.AppendFormat("System:\t{0}", !string.IsNullOrEmpty(this.System.Name) ? this.System.Name : "-");
            logData.AppendLine();
            logData.AppendFormat("Source:\t{0}", !string.IsNullOrEmpty(this.Source.Name) ? this.Source.Name : "-");
            logData.AppendLine();
            logData.AppendFormat("Type:\t{0}", this.Type);
            logData.AppendLine();
            logData.AppendFormat("Date:\t{0:yyyy-MM-dd HH:mm:ss.ffffff}", this.Date);
            logData.AppendLine();

            logData.AppendLine();
            logData.AppendLine("Message:");
            logData.AppendLine(SEPARATOR);
            logData.AppendLine(this.Message.ToString());

            logData.AppendLine();
            logData.AppendLine("Enviroment:");
            logData.AppendLine(SEPARATOR);
            logData.Append(this.Enviroment.ToString());

            if (this.ParameterErrors != null && this.ParameterErrors.Count > 0)
            {
                logData.AppendLine();
                logData.AppendLine("Parameter Errors:");
                logData.AppendLine(SEPARATOR);
                logData.AppendLine(string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.ParameterErrors.ToArray()));
            }

            if (this.ConfigurationErrors != null && this.ConfigurationErrors.Count > 0)
            {
                logData.AppendLine();
                logData.AppendLine("Configuration Errors:");
                logData.AppendLine(SEPARATOR);
                logData.AppendLine(string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.ConfigurationErrors.ToArray()));
            }

            if (this.WriteErrors != null && this.WriteErrors.Count > 0)
            {
                logData.AppendLine();
                logData.AppendLine("Write Errors:");
                logData.AppendLine(SEPARATOR);
                logData.AppendLine(string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.WriteErrors.ToArray()));
            }

            if (this.DisplayErrors != null && this.DisplayErrors.Count > 0)
            {
                logData.AppendLine();
                logData.AppendLine("Display Errors:");
                logData.AppendLine(SEPARATOR);
                logData.AppendLine(string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.DisplayErrors.ToArray()));
            }

            if (this.PackagerErrors != null && this.PackagerErrors.Count > 0)
            {
                logData.AppendLine();
                logData.AppendLine("Packager Errors:");
                logData.AppendLine(SEPARATOR);
                logData.AppendLine(string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.PackagerErrors.ToArray()));
            }

            if (!string.IsNullOrEmpty(this.InnerLogData))
            {
                logData.AppendLine(SEPARATOR);
                logData.AppendLine();
                logData.AppendLine("Inner Log Data:");
                logData.AppendLine(SEPARATOR);
                logData.Append(this.InnerLogData);
            }

            return logData.ToString();
        }

        public string ToXml()
        {
            StringBuilder sb = new StringBuilder();
            XmlWriter xml = new XmlTextWriter(new StringWriter(sb));

            xml.WriteStartElement("TraceRecord");
            xml.WriteElementString("System", this.System.Name);
            xml.WriteElementString("Source", this.Source.Name);
            xml.WriteElementString("Type", this.Type.ToString());
            xml.WriteElementString("Date", string.Format("{0:yyyy-MM-dd HH:mm:ss.ffffff}", this.Date));
            
            if (this.Message is string)
                xml.WriteElementString("Message", this.Message.ToString());
            
            xml.WriteElementString("AppDomain", AppDomain.CurrentDomain.FriendlyName);

            this.Enviroment.WriteTo(xml);
            

            if (this.Message is Exception)
            {
                xml.WriteStartElement("Exception");
                AddExceptionToTraceString(xml, this.Message as Exception);
                xml.WriteEndElement();
            }


            if (this.ParameterErrors != null && this.ParameterErrors.Count > 0)
            {
                xml.WriteElementString("ParameterErrors", string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.ParameterErrors.ToArray()));
            }

            if (this.ConfigurationErrors != null && this.ConfigurationErrors.Count > 0)
            {
                xml.WriteElementString("ConfigurationErrors", string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.ConfigurationErrors.ToArray()));
            }

            if (this.WriteErrors != null && this.WriteErrors.Count > 0)
            {
                xml.WriteElementString("WriteErrors", string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.WriteErrors.ToArray()));
            }

            if (this.DisplayErrors != null && this.DisplayErrors.Count > 0)
            {
                xml.WriteElementString("DisplayErrors", string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.DisplayErrors.ToArray()));
            }

            if (this.PackagerErrors != null && this.PackagerErrors.Count > 0)
            {
                xml.WriteElementString("PackagerErrors", string.Join(Environment.NewLine + SEPARATOR + Environment.NewLine, this.PackagerErrors.ToArray()));
            }

            if (!string.IsNullOrEmpty(this.InnerLogData))
            {
                xml.WriteElementString("InnerLogData", this.InnerLogData);
            }

            xml.WriteEndElement();

            return sb.ToString();
        }

        private void AddExceptionToTraceString(XmlWriter xml, Exception exception)
        {
            xml.WriteElementString("ExceptionType", XmlEncode(exception.GetType().AssemblyQualifiedName));
            xml.WriteElementString("Message", XmlEncode(exception.Message));
            xml.WriteElementString("StackTrace", XmlEncode(StackTraceString(exception)));
            xml.WriteElementString("ExceptionString", XmlEncode(exception.ToString()));
            Win32Exception exception2 = exception as Win32Exception;
            if (exception2 != null)
            {
                xml.WriteElementString("NativeErrorCode", exception2.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture));
            }
            if ((exception.Data != null) && (exception.Data.Count > 0))
            {
                xml.WriteStartElement("DataItems");
                foreach (object obj2 in exception.Data.Keys)
                {
                    if (obj2 != null && exception.Data[obj2] != null)
                    {
                        xml.WriteStartElement("Data");
                        xml.WriteElementString("Key", XmlEncode(obj2.ToString()));
                        xml.WriteElementString("Value", XmlEncode(exception.Data[obj2].ToString()));
                        xml.WriteEndElement();
                    }
                }
                xml.WriteEndElement();
            }
            if (exception.InnerException != null)
            {
                xml.WriteStartElement("InnerException");
                AddExceptionToTraceString(xml, exception.InnerException);
                xml.WriteEndElement();
            }
        }

        private string StackTraceString(Exception exception)
        {
            string stackTrace = exception.StackTrace;
            if (!string.IsNullOrEmpty(stackTrace))
            {
                return stackTrace;
            }
            StackFrame[] frames = new StackTrace(false).GetFrames();
            int skipFrames = 0;
            bool flag = false;
            foreach (StackFrame frame in frames)
            {
                string str3;
                string name = frame.GetMethod().Name;
                if (((str3 = name) != null) && (((str3 == "StackTraceString") || (str3 == "AddExceptionToTraceString")) || (((str3 == "BuildTrace") || (str3 == "TraceEvent")) || (str3 == "TraceException"))))
                {
                    skipFrames++;
                }
                else if (name.StartsWith("ThrowHelper", StringComparison.Ordinal))
                {
                    skipFrames++;
                }
                else
                {
                    flag = true;
                }
                if (flag)
                {
                    break;
                }
            }
            StackTrace trace = new StackTrace(skipFrames, false);
            return trace.ToString();
        }

        private string XmlEncode(string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }
            int length = text.Length;
            StringBuilder builder = new StringBuilder(length + 8);
            for (int i = 0; i < length; i++)
            {
                char ch = text[i];
                switch (ch)
                {
                    case '<':
                        {
                            builder.Append("&lt;");
                            continue;
                        }
                    case '>':
                        {
                            builder.Append("&gt;");
                            continue;
                        }
                    case '&':
                        {
                            builder.Append("&amp;");
                            continue;
                        }
                }
                builder.Append(ch);
            }
            return builder.ToString();
        }

        [OnSerializing]
        private void Serializing(StreamingContext context)
        {
            // Code not shown.
        }

        [OnDeserializing]
        private void Deserializing(StreamingContext context)
        {
            // Code not shown.
        }
    }
}
